package com.jackeys.piaems.util;

import org.springframework.beans.factory.annotation.Value;

/**
 * @ClassName: SnowFlakeIdUtil
 * @Description: 利用雪花算法生成id
 * @Author: Jackeys
 * @Date: 2021-06-14 18:54
 * @Version: 1.0
 */
public class SnowFlakeIdUtil {


    //机器ID
    private  static Long workId=1L;
    //机房ID
    private  static Long datacenterId=2L;
    //一毫秒生成的多个id的最新序号
    private  static Long sequence=100L;
    //时间初始值
    private  static Long iniTime = 1585644268888L;
    //5位机器ID
    private  static Long workerIdBits = 5L;
    //5位机房ID
    private  static Long datacenterIdBits = 5L;
    //每毫秒产生id数的2的12次方
    private  static Long sequenceBits = 12L;
    //最大机器ID
    private  static Long maxWorkerId = -1L ^ (-1L << workerIdBits);
    //最大机房ID
    private  static Long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

    private  static Long workerIdShift = sequenceBits;
    private  static Long datacenterIdShift = sequenceBits + workerIdBits;
    private  static Long timeStampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    private  static Long sequenceMask = -1L ^ (-1L << sequenceBits);
    //记录时间毫秒判断是否为同一毫秒
    private  static Long lastTimeStamp = -1L;

    public SnowFlakeIdUtil(Long workerId, Long datacenterId, Long sequence) {
        if (workerId > maxWorkerId || maxWorkerId < 0) {
            throw new IllegalArgumentException(
                    String.format("机器ID不能超过%d和小于0", maxWorkerId)
            );
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(
                    String.format("机房ID不能超过%d和小于0", maxDatacenterId)
            );
        }
        this.workId = workerId;
        this.datacenterId = datacenterId;
        this.sequence = sequence;
    }

    //生成唯一ID
    public  static synchronized Long nextId() {
        Long timeStamp = System.currentTimeMillis();
        if (timeStamp < lastTimeStamp) {
            System.err.printf("错误！，时间已被回调");
            throw new RuntimeException(
                    String.format("时间已被回调%d秒", lastTimeStamp - timeStamp)
            );
        }
        //如果在同一毫秒
        if (lastTimeStamp.equals(timeStamp)) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timeStamp = tilNextMillis(lastTimeStamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimeStamp = timeStamp;
        return ((timeStamp - iniTime) << timeStampLeftShift) | (datacenterId << datacenterIdShift) | (workId << workerIdShift) | sequence;
    }

    private static Long tilNextMillis(Long lastTimeStamp) {
        Long now = System.currentTimeMillis();
        while (now <= lastTimeStamp) {
            now = System.currentTimeMillis();
        }
        return now;
    }
}
